package cn.akwangl.controller.async;

import cn.akwangl.common.Result;
import cn.akwangl.interceptor.SystemInterceptor;
import cn.akwangl.service.async.AsyncService;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.interceptor.AsyncExecutionInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.Callable;

/**
 * 拦截器与@Async 演示.
 * <p>
 * 在这里我首先抛出一个问题,在{@link SystemInterceptor#getGlobalUser()}(ThreadLocal声明的变量)在每次Http请求处理前都会解析Token并放到ThreadLocal中,
 * 在请求结束时会将ThreadLocal中数据给清除调,那么在使用{@link Async}时,会不会在方法异调用开始{@link SystemInterceptor#getGlobalUser()}中的
 * 数据就被清除了呢?<br/>
 * 这里可以先给出结论是会被清除的,在调用被标注{@link Async}注解的方法时,Spring会去调用{@link AsyncExecutionInterceptor#invoke(MethodInvocation)}
 * 方法将任务包装在一个{@link Callable}类中提交到线程池中,程序走到invoke这个方法体时线程与拦截器中的线程还是同一个(在这时{@link SystemInterceptor#afterCompletion}还不会被执行),
 * 等这个方法调用{@code return this.doSubmit(task, executor, invocation.getMethod().getReturnType());}语句时,
 * 刚好这个方法处于controller最后语句,那么拦截器“afterCompletion”销毁方法会被执行,刚好被标注{@link Async}方法中依赖了{@link SystemInterceptor#getGlobalUser()}变量,所以存在空指针问题.
 * <p/>
 *
 * @author 阿坤同学
 * @date 2022/5/26 9:31
 * @since 0.0.4
 */
@RestController
@RequestMapping("/test")
public class AsyncControllerTest {

    private AsyncService asyncService;

    @Lazy
    @Autowired
    public void setAsyncService(AsyncService asyncService) {
        this.asyncService = asyncService;
    }

    @GetMapping("/async")
    public Result asyncTest() {
        asyncService.doAsync();
        return Result.success();
    }
}